Ana içeriğe geç

Closures

Closures anonim fonkiyonlardır. Bu da isimleri olmayan fonksiyonlar anlamına gelmektedir. Bu yapılar içerilerinde değişken tutabilen olmaktadır. Hadi bir örnek program ile inceleyelim.

İlk olarak bir struct yapısı oluşturarak başlayabiliriz.

struct City {
city: String,
population: u64
}

Sonrasında bu veri yapısını vektör olarak içerisine kabul ederek sıralayan bir fonksiyon oluşturabiliriz.

fn sort_pop(city: &mut Vec<City>){
city.sort()
}

Bu yapıyı oluşturduğumuzda bir hata ile karşılaştığımızı görüntüleyebilirsiniz. Bu durumun nedeni bir yardımcı fonksiyona ihtiyaç duymamızdır.

fn pop_helper(pop: &City) -> u64 {
pop.population
}

Sonrasında artık sadece sort demek yerine sort_by_key methodunu çağırıp işlemlerimizi yapabiliriz.

fn sort_pop(city: &mut Vec<City>){
city.sort_by_key(pop_helper)
}

Artık şehirlerimizi oluşturup çağırabiliriz.

fn main(){
let a = City{city: String::from("Ankara"), population: 100};
let b = City{city: String::from("Bursa"), population: 57};
let c = City{city: String::from("Ceyhan"), population: 140};
let d = City{city: String::from("Diyarbakır"), population: 15};
let e = City{city: String::from("Edirne"), population: 70};

let mut vec: Vec<City> = Vec::new();
vec.push(a);
vec.push(b);
vec.push(c);
vec.push(d);
vec.push(e);
}

Vektörümüzü oluşturduğumuza göre artık sort methodumuzu kullanıp vektörümüzü sıralayabiliriz.

sort_pop(&mut vec);
println!("{:?}", vec);

Ayrıca kodun başlangıcına da debug modunu eklememiz gerekmektedir.

#[derive(Debug)]

Kodumuzu artık çalıştırabiliriz.

[City { city: "Diyarbakır", population: 15 }, City { city: "Bursa", population: 57 }, City { city: "Edirne", population: 70 }, City { city: "Ankara", population: 100 }, City { city: "Ceyhan", population: 140 }]

Görüntülendiği üzere sıkıntız bir şekilde programımız çalıştı. Ancak bu işlemleri biz closures yapıları kullanarak da gerçekleştirebilirdik. Bu işlemi yapmak için |x|yapısını kullanabiliriz. Hadi yeni bir fonksiyon oluşturarak bakalım.

fn sort_pop_closure(pop: &mut Vec<City>){
pop.sort_by_key(|p| p.population)
}

Kodumuzu inceleyecek olursak aslında sort_by_key içerisinde mikro bir fonksiyon yapmış oluruz. Bu yapı biraz JavaScript içerisindeki =>{} yapısına benzetilebilir. P adında bir fonksiyon değişkeni oluşturup bu değişken sayesinde fonksiyona girilen pop değeri içerisindeki population değerine erişebiliriz. Bu sayede yardımcı fonksiyonlara gerek kalmamış oluruz.

Sonrasında da çağırdığımız fonksiyonu değişirsek:

sort_pop_closure(&mut vec);
println!("{:?}", vec);

Kodumuzu çalıştırdığımızda aynı sonuç ile karşılaştığımızı görüntüleyebiliriz.

[City { city: "Diyarbakır", population: 15 }, City { city: "Bursa", population: 57 }, City { city: "Edirne", population: 70 }, City { city: "Ankara", population: 100 }, City { city: "Ceyhan", population: 140 }]

Fark etmiş olabileceğiniz üzere closures içerisine aldığı değişkenin tipinin direkt olarak belirtilmesine ihtiyaç duymazlar. Bu durumun nedeni closures değerlerin genellikle kısa ve ince contexlere sahip olmasıdır. Ancak istersek bizde kendimiz bu tipleri ifade edebiliriz.

Hadi bir örnekle bu yapıları görüntüleyelim.

let ekle = |x i32| -> i32 {x + 1};
let ekle_v2 = |x| x + 1;

İki kod da aynı işlemi yapmaktadır ve çalışmaktadır. Ancak bu şekilde bırakırsak ekle_v2 yapısının hata verdiğini görüntüleyebiliriz. Bu hatayı tür belirterek çözümleyebilsek de onun yerine fonsiyonu içerisine veri girerek çalıştısak da sorun çözülmüş olacaktır.

ekle_v2(1);

Ancak dikkat etmemiz gereken şey tip tanımlama veya fonksiyon çağırma ile compiler'a hangi tipte çalışacağını belirttikten sonra farklı bir tipe geçiş yapamayız.

Closures yapıları hızlı olmak için tasarlanıp stack içerisinde tutulmaktadır.